/*
 * Copyright (c) 2025 Huawei Device, Inc. Ltd. and <马弓手>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "AudioPlayer.h"
#include "AudioHandler.h"
#include <fcntl.h>
#include <sys/stat.h>
#include "cJSON.h"
#include "hilog/log.h"
AudioPlayer::AudioPlayer(void * pHandler, const std::string& strId) : m_currentState(State::RELEASED), m_fCurrentSpeed(1.0f),m_pHandler(pHandler),m_strId(strId) {
    
}

AudioPlayer::~AudioPlayer() {
}

bool AudioPlayer::create() {
    // 创建 AVPlayer 实例
    m_player = OH_AVPlayer_Create();
    if (!m_player) {
        m_currentState = State::ERROR;
        return false;
    }
    m_currentState = State::IDLE;
    // 设置回调
    OH_AVPlayer_SetOnInfoCallback(m_player, AudioPlayer::PlayerOnInfoCallback, this);
    OH_AVPlayer_SetOnErrorCallback(m_player, AudioPlayer::PlayerOnErrorCallback, this);
    return true;
}

bool AudioPlayer::release()
{
    if (m_player) {
        OH_AVPlayer_Release(m_player);
    }
    m_player = nullptr;
    m_currentState = State::RELEASED;
    return true;
}

bool AudioPlayer::setSource(const std::string &url) {
    if (!m_player || m_currentState != State::IDLE) {
        return false;
    }

    // 设置数据源
    int32_t ret = 0;
    if(url.find("http") == 0 || url.find("rtsp") == 0) {
        ret = OH_AVPlayer_SetURLSource(m_player, url.c_str());
    } else if(url.find("/data") == 0){
        struct stat s_buf;
        stat(url.c_str(), &s_buf);
        if(!S_ISREG(s_buf.st_mode)){
            return false;
        }
        long lngSize = s_buf.st_size;
        int fd = open(url.c_str(), O_RDONLY);
        ret = OH_AVPlayer_SetFDSource(m_player, fd, 0, lngSize);
    }
    
    if (ret != AV_ERR_OK) {
        m_currentState = State::ERROR;
        return false;
    }

    m_currentState = State::INITIALIZED;
    return true;
}

bool AudioPlayer::prepare() {
    if (!m_player || m_currentState != State::INITIALIZED) {
        return false;
    }

    int32_t ret = OH_AVPlayer_Prepare(m_player);
    if (ret != AV_ERR_OK) {
        m_currentState = State::ERROR;
        return false;
    }
    m_currentState = State::PREPARED;
    return true;
}

bool AudioPlayer::play() {
    if (!m_player || (m_currentState != State::PREPARED && m_currentState != State::PAUSED)) {
        char szBuf[1024];
        sprintf(szBuf, "AudioPlayer Error:startPlaying() called during invalid state:%d", m_currentState);
        sendErrorStatus(const_media_err_none_active, szBuf);
        return false;
    }

    int32_t ret = OH_AVPlayer_Play(m_player);
    if (ret != AV_ERR_OK) {
        m_currentState = State::ERROR;
        return false;
    }

    m_currentState = State::PLAYING;
    return true;
}

bool AudioPlayer::pause() {
    if (!m_player || m_currentState != State::PLAYING) {
        char szBuf[1024];
        sprintf(szBuf, "AudioPlayer Error:pausePlaying() called during invalid state:%d", m_currentState);
        sendErrorStatus(const_media_err_none_active, szBuf);
        return false;
    }

    int ret = OH_AVPlayer_Pause(m_player);
    if (ret != AV_ERR_OK) {
        m_currentState = State::ERROR;
        return false;
    }

    m_currentState = State::PAUSED;
    return true;
}

bool AudioPlayer::stop() {
    if (!m_player || m_currentState != State::PLAYING) {
        char szBuf[1024];
        sprintf(szBuf, "AudioPlayer Error:stopPlaying() called during invalid state:%d", m_currentState);
        sendErrorStatus(const_media_err_none_active, szBuf);
        return false;
    }

    int ret = OH_AVPlayer_Stop(m_player);
    if (ret != AV_ERR_OK) {
        m_currentState = State::ERROR;
        return false;
    }

    m_currentState = State::STOPPED;
    return true;
}

bool AudioPlayer::reset()
{
    if (!m_player) {
        return false;
    }

    int ret = OH_AVPlayer_Reset(m_player);
    if (ret != AV_ERR_OK) {
        m_currentState = State::ERROR;
        return false;
    }

    m_currentState = State::IDLE;
    return true;
}

bool AudioPlayer::seekTo(long mSec) {
    if (!m_player || (m_currentState != State::PLAYING && m_currentState != State::PAUSED)) {
        char szBuf[1024];
        sprintf(szBuf, "AudioPlayer Error:seekTo() called during invalid state:%d", m_currentState);
        sendErrorStatus(const_media_err_none_active, szBuf);
        return false;
    }

    int ret = OH_AVPlayer_Seek(m_player, mSec, AV_SEEK_CLOSEST);
    if (ret != AV_ERR_OK) {
        return false;
    }
    sendStatusChange(const_media_position, -1, mSec/1000, "");
    return true;
}

bool AudioPlayer::setVolume(float fVolume) {
    if (!m_player || (m_currentState != State::PLAYING && m_currentState != State::PAUSED)) {
        char szBuf[1024];
        sprintf(szBuf, "AudioPlayer Error:setVolume() called during invalid state:%d", m_currentState);
        sendErrorStatus(const_media_err_none_active, szBuf);
        return false;
    }
    
    int ret = OH_AVPlayer_SetVolume(m_player, fVolume, fVolume);
    if(ret != AV_ERR_OK) {
        return false;
    }
    return true;
}

// 设置倍速播放（0.5x ~ 2.0x） api12 版本
bool AudioPlayer::setPlaybackSpeed(float fSpeed) {
    if (!m_player || (m_currentState != State::PLAYING && m_currentState != State::PAUSED)) {
        char szBuf[1024];
        sprintf(szBuf, "AudioPlayer Error:setPlaybackSpeed() called during invalid state:%d", m_currentState);
        sendErrorStatus(const_media_err_none_active, szBuf);
        return false;
    }

    // 检查倍速范围（鸿蒙一般支持 0.5x ~ 2.0x）
    if (fSpeed < 0.5f || fSpeed > 2.0f) {
        return false;
    }
    
    int nTmpSpeed = static_cast<int>(fSpeed*100);
    AVPlaybackSpeed avSpeed = AV_SPEED_FORWARD_1_00_X;
    if(nTmpSpeed >=50 && nTmpSpeed < 75) {
        avSpeed = AV_SPEED_FORWARD_0_50_X;
    } else if(nTmpSpeed >=75 && nTmpSpeed < 100) {
        avSpeed = AV_SPEED_FORWARD_0_75_X;
    } else if(nTmpSpeed >=100 && nTmpSpeed < 125) {
        avSpeed = AV_SPEED_FORWARD_1_25_X;
    } else if(nTmpSpeed >=175 && nTmpSpeed < 200) {
        avSpeed = AV_SPEED_FORWARD_1_75_X;
    } else if(nTmpSpeed >=200) {
        avSpeed = AV_SPEED_FORWARD_2_00_X;
    } 

    int32_t ret = OH_AVPlayer_SetPlaybackSpeed(m_player, avSpeed);
    if (ret != AV_ERR_OK) {
        return false;
    }

    m_fCurrentSpeed = fSpeed;
    return true;
}

// 获取当前倍速
float AudioPlayer::getCurrentSpeed() const {
    return m_fCurrentSpeed;
}

bool AudioPlayer::getCurrentPosition(int& nPosition) {
    int32_t ret = OH_AVPlayer_GetCurrentTime(m_player, &nPosition);
    if (ret != AV_ERR_OK) {
        return false;
    }
    sendStatusChange(const_media_position, -1, nPosition/1000, "");
    return true;
}
bool AudioPlayer::getDuration(int& nPosition) const {
    int32_t ret = OH_AVPlayer_GetDuration(m_player, &nPosition);
    if (ret != AV_ERR_OK) {
        return false;
    }
    return true;
}

AudioPlayer::State AudioPlayer::getState() const {
    return m_currentState;
}

void AudioPlayer::setState(AudioPlayer::State state){
    m_currentState = state;
}

void* AudioPlayer::getHandler() {
    return m_pHandler;
}

void AudioPlayer::sendErrorStatus(int nErrorCode, std::string strErrorMessage) {
    sendStatusChange(const_media_error, nErrorCode, -1,strErrorMessage);
}

void AudioPlayer::sendStatusChange(int nMessageType, int nAdditionalCode, float fValue, std::string strErrorMessage) {
    cJSON* pStatusDetails = cJSON_CreateObject();
    cJSON_AddStringToObject(pStatusDetails, "id", m_strId.c_str());
    cJSON_AddNumberToObject(pStatusDetails, "msgType", nMessageType);
    if(nAdditionalCode != -1) {
        cJSON_AddNumberToObject(pStatusDetails, "code", nAdditionalCode);
        if(!strErrorMessage.empty()) {
            cJSON_AddStringToObject(pStatusDetails, "message", strErrorMessage.c_str());
        }
        cJSON_AddNumberToObject(pStatusDetails, "value", fValue);
    } else if(fValue >-1E-6) {
        cJSON_AddNumberToObject(pStatusDetails, "value", fValue);
    }
    AudioHandler* pHandler = (AudioHandler*)m_pHandler;
    pHandler->sendEventMessage("status", pStatusDetails);
}

void AudioPlayer::PlayerOnInfoCallback(OH_AVPlayer *player, AVPlayerOnInfoType type, OH_AVFormat* infoBody,void *userData) {
    AudioPlayer* pAudioPlayer = (AudioPlayer*)userData;
    int32_t value = -1;

    int32_t state = -1;
    int32_t stateChangeReason = -1;
    AVPlayerState avState = AV_IDLE;

    float volume = 0.0;

    int32_t width = -1;
    int32_t height = -1;

    int32_t bufferType = -1;
    int32_t bufferValue = -1;

    uint8_t *bitRates;
    size_t size;

    int32_t interruptType = -1;
    int32_t interruptForce = -1;
    int32_t interruptHint = -1;
    switch (type) {
    case AV_INFO_TYPE_STATE_CHANGE:
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "AVPlayerOnInfoType AV_INFO_TYPE_STATE_CHANGE");
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_STATE, &state);
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_STATE_CHANGE_REASON, &stateChangeReason);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_STATE_CHANGE  state: %{public}d ,stateChangeReason: %{public}d", 
            state, stateChangeReason);
        avState = static_cast<AVPlayerState>(state);
        switch (avState) {
        case AV_IDLE: // 成功调用reset接口后触发该状态机上报
            OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "AVPlayerState  AV_IDLE");
            break;
        case AV_INITIALIZED: 
            OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "AVPlayerState  AV_INITIALIZED");
            break;
        case AV_PREPARED: {
            OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "AVPlayerState AV_PREPARED");
            int nDuration = 0;
            if(pAudioPlayer->getDuration(nDuration)) {
                pAudioPlayer->sendStatusChange(const_media_duration, -1, nDuration/1000, "");
            }
            }break;
        case AV_PLAYING:
            OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "AVPlayerState AV_PLAYING");
            break;
        case AV_PAUSED:
            OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "AVPlayerState AV_PAUSED");
            break;
        case AV_STOPPED:
            OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "AVPlayerState AV_STOPPED");
            break;
        case AV_COMPLETED:
            OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "AVPlayerState AV_COMPLETED");
            break;
        case AV_ERROR:
            OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "AVPlayerState AV_ERROR");
            break;
        case AV_RELEASED:
            OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "AVPlayerState AV_RELEASED");
            break;
        default:
            break;
        }
        break;
    case AV_INFO_TYPE_SEEKDONE:
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_SEEK_POSITION, &value);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_SEEKDONE value: %{public}d", value);
        break;
    case AV_INFO_TYPE_SPEEDDONE:
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_PLAYBACK_SPEED, &value);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_SPEEDDONE value: %{public}d", value);
        break;
    case AV_INFO_TYPE_BITRATEDONE:
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_BITRATE, &value);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_BITRATEDONE value: %{public}d", value);
        break;
    case AV_INFO_TYPE_EOS:
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_EOS");
        pAudioPlayer->setState(AudioPlayer::COMPLETED);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "play completed");
        break;
    case AV_INFO_TYPE_POSITION_UPDATE:
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_CURRENT_POSITION, &value);
        // 以下log会频繁打印
        // OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_POSITION_UPDATE value: %{public}d", value);
        break;
    case AV_INFO_TYPE_MESSAGE:
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_MESSAGE_TYPE, &value);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_MESSAGE value: %{public}d", value);
        break;
    case AV_INFO_TYPE_VOLUME_CHANGE:
        OH_AVFormat_GetFloatValue(infoBody, OH_PLAYER_VOLUME, &volume);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_VOLUME_CHANGE value: %{public}f", volume);
        break;
    case AV_INFO_TYPE_RESOLUTION_CHANGE:
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_VIDEO_WIDTH, &width);
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_VIDEO_HEIGHT, &height);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_RESOLUTION_CHANGE width: %{public}d, height: %{public}d", 
            width, height);
        break;
    case AV_INFO_TYPE_BUFFERING_UPDATE:
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_BUFFERING_TYPE, &bufferType);
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_BUFFERING_VALUE, &bufferValue);
        // 以下log会频繁打印
//        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_BUFFERING_UPDATE bufferType: %{public}d, bufferValue: %{public}d",
//            bufferType, bufferValue);
        break;
    case AV_INFO_TYPE_BITRATE_COLLECT:
        OH_AVFormat_GetBuffer(infoBody, OH_PLAYER_BITRATE_ARRAY, &bitRates, &size);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_BITRATE_COLLECT size: %{public}zu", size);
        for (size_t i = 0, cnt = size / sizeof(uint32_t); i < cnt; i++) {
            OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_BITRATE_COLLECT bitRates[%{public}zu]: %{public}u", i,
                *(static_cast<uint32_t *>(static_cast<void *>(bitRates)) + i));
        }
        break;
    case AV_INFO_TYPE_INTERRUPT_EVENT:
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_AUDIO_INTERRUPT_TYPE, &interruptType);
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_AUDIO_INTERRUPT_FORCE, &interruptForce);
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_AUDIO_INTERRUPT_HINT, &interruptHint);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_INTERRUPT_EVENT interruptType: %{public}d, "
            "interruptForce: %{public}d, interruptHint: %{public}d", interruptType, interruptForce, interruptHint);
        break;
    case AV_INFO_TYPE_DURATION_UPDATE:
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_DURATION, &value);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_DURATION_UPDATE value: %{public}d", value);
        break;
    case AV_INFO_TYPE_IS_LIVE_STREAM:
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_IS_LIVE_STREAM, &value);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_IS_LIVE_STREAM value: %{public}d", value);
        break;
    case AV_INFO_TYPE_TRACKCHANGE:
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_TRACKCHANGE value: %{public}d", value);
        break;
    case AV_INFO_TYPE_TRACK_INFO_UPDATE:
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_TRACK_INFO_UPDATE value: %{public}d", value);
        break;
    case AV_INFO_TYPE_SUBTITLE_UPDATE:
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_SUBTITLE_UPDATE value: %{public}d", value);
        break;
    case AV_INFO_TYPE_AUDIO_OUTPUT_DEVICE_CHANGE:
        OH_AVFormat_GetIntValue(infoBody, OH_PLAYER_AUDIO_DEVICE_CHANGE_REASON, &value);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "ArkWeb", "OHAVPlayerOnInfoCallback AV_INFO_TYPE_AUDIO_OUTPUT_DEVICE_CHANGE value: %{public}d", value);
        break;
    default:
        break;
    }
}

void AudioPlayer::PlayerOnErrorCallback(OH_AVPlayer *player, int32_t errorCode, const char *errorMsg,void *userData) 
{
    AudioPlayer* pAudioPlayer = (AudioPlayer*)userData;
    pAudioPlayer->sendErrorStatus(errorCode, errorMsg);
    OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "ArkWeb", "PlayerOnErrorCallback:errorCode:%{public}d,errorMsg:%{public}s", errorCode, errorMsg);
}